#include "SFC_Inc.hpp"
#include "Trace.h"
#include "Platform.h"
#include "SFC_WindowServer.h"

SFC_WindowServer *WindowManager;

extern void InitIOSystem();

SFC_WindowServer::SFC_WindowServer(std::string Title, int Width, int Height)
{
	InitIOSystem();
	OutsideSystem->CreateHostInterface(Title.c_str(), Width, Height);
	this->Size = {0, 0, Width, Height};
	/* Create cursor surface */
	this->Cursor = new SFC_Point(this->Size.w / 2, this->Size.h / 2);

	this->CursorSurface = CreateSurface(CURSOR_SIZE, CURSOR_SIZE);
	FillRect(this->CursorSurface, nullptr, 0x00000000);
	float dx = 1, dy = 1, len = 1;
	while (dy <= 20)
	{
		DrawLine(this->CursorSurface, dx, dy, dx + len, dy, 0xFF000000);
		len += (dy < 10)? 1.5 : -1.5;
		dy++; dx += 0.5;
	}
	SFC_Point Lines[] = {{0, 0}, {10, 20}, {20, 10}, {0, 0}};
	DrawLines(this->CursorSurface, Lines, 4, 0xFFFFFFFF);

	/* Init trace layer */
	trace.Setk(10, this->Size.h - 200, this->Size.w - 20, 180);
	trace << "trace start ..." << std::endl;

	this->ShutdownButton = new SFC_Button();
	this->Children.push_back(this->ShutdownButton);
	this->ShutdownButton->Parent = this;
	this->ShutdownButton->OnClick += { this, &SFC_WindowServer::Shutdown };
	this->ShutdownButton->Text = "Shutdown";
	this->ShutdownButton->Size = {0, 0, 120, 35};
	this->ShutdownButton->Durty = true;

	this->ChangeVisiableButton = new SFC_Button();
	this->Children.push_back(this->ChangeVisiableButton);
	this->ChangeVisiableButton->Parent = this;
	this->ChangeVisiableButton->OnClick += { this, &SFC_WindowServer::ChangeVisiable };
	this->ChangeVisiableButton->Text = "Visiable";
	this->ChangeVisiableButton->Size = {0, 37, 120, 35};
	this->ChangeVisiableButton->Durty = true;

	LoadRes();
}

SFC_WindowServer::~SFC_WindowServer()
{
}

void SFC_WindowServer::AddWindow(SFC_Window *window)
{
	USE_WINDOW_LIST(wlptr);
	for (auto wptr = wlptr->begin(); wptr != wlptr->end(); wptr++)
	{
		if (*wptr == window)
			return ;
	}
	wlptr->push_back(window);
	window->wthread = std::thread([window](){window->StartListen();});
}

void SFC_WindowServer::RemoveWindow(SFC_Window *window)
{
	USE_WINDOW_LIST(wlptr);
	USE_EXPIRING_LIST(elptr);
	for (auto wptr = wlptr->begin(); wptr != wlptr->end(); wptr++)
	{
		if (*wptr == window) {
			wlptr->erase(wptr);
			elptr->push(window);
			break;
		}
	}
}

void SFC_WindowServer::ProcExpiring()
{
	USE_EXPIRING_LIST(elptr);
	if (elptr->empty() == false)
	{
		SFC_Window *wptr = elptr->front();
		elptr->pop();
		if (wptr->wthread.joinable())
		{
			wptr->wthread.join();
		}
		delete wptr;
	}
}

void SFC_WindowServer::EventRouter(SFC_Event &Event)
{
	if (Event.Type == EventType::Power) {
		switch (Event.Power.Type)
		{
		case PowerEventType::Shutdown:
			this->Runable = false;
			break;
		default:
			break;
		}
		return ;
	} else if (Event.Type == EventType::Mouse) {
		this->Cursor->x = Event.Forcus.x;
		this->Cursor->y = Event.Forcus.y;
	} else if (Event.Type == EventType::Window && Event.Window.Type == WindowEventType::Paint) {
		Paint();
	}
	// Send Event
	USE_WINDOW_LIST(wlptr);
	for (auto wptr = wlptr->begin(); wptr != wlptr->end(); wptr++)
	{
		if ((*wptr)->Size.Inside(Event.Forcus))
		{
			Event.Forcus.x -= (*wptr)->Size.x;
			Event.Forcus.y -= (*wptr)->Size.y;
			(*wptr)->MsgQueue.Put(Event);
		}
	}
	if (this->ShutdownButton->Size.Inside(Event.Forcus))
	{
		this->ShutdownButton->EventRouter(Event);
	} else if (this->ChangeVisiableButton->Size.Inside(Event.Forcus)) {
		this->ChangeVisiableButton->EventRouter(Event);
	}
}

bool SFC_WindowServer::Shutdown()
{
	SFC_Event ShutdownEvent;
	ShutdownEvent.Type = EventType::Power;
	ShutdownEvent.Power.Type = PowerEventType::Shutdown;
	OutsideSystem->EventQueue.Put(ShutdownEvent);
	return true;
}

bool SFC_WindowServer::ChangeVisiable()
{
	std::list<SFC_Window*> *wlptr = (std::list<SFC_Window*>*)&this->WindowList;
	for (auto wptr = wlptr->begin(); wptr != wlptr->end(); wptr++)
	{
		(*wptr)->Visiable = !(*wptr)->Visiable;
	}
	return true;
}

void SFC_WindowServer::WaitExit()
{
	std::cout << "WaitExit" << std::endl;
	//std::thread DrawThread([this](){this->AutoRefresh(nullptr);});
	this->Runable = true;
	OutsideSystem->StartListenWithThread();
	while (this->Runable)
	{
		ProcExpiring();
		SFC_Event e = OutsideSystem->EventQueue.Get();
		EventRouter(e);
	}
	OutsideSystem->StopListenWithThread();
	trace << "Wait drawing thread exit ..." << std::endl;
	this->Drawable = false;
	//DrawThread.join();
}

void SFC_WindowServer::LoadRes()
{
	// Create cache surface
	if (this->Cache == nullptr)
		this->Cache = CreateSurface(this->Size.w, this->Size.h);
	FillRect(this->Cache, nullptr, 0xFF000077);

	this->Durty = false;
}

void SFC_WindowServer::Paint()
{
	if (this->Durty == false) return ;

	LoadRes();
}

int SFC_WindowServer::AutoRefresh(void*)
{
	this->MainSurface = OutsideSystem->GetBufferSurface();
	Uint32 tick = clock() / CLOCKS_PER_SEC * 1000, FPS = 20;
	int NeedDelay = 0;
	this->Drawable = true;
	while (this->Drawable)
	{
		Refresh();

		NeedDelay = FPS - (clock() / CLOCKS_PER_SEC * 1000 - tick);
		if (NeedDelay > 0) { OutsideSystem->Delay(NeedDelay); }

	}
	FreeSurface(this->MainSurface);
	this->MainSurface = 0;
	return 0;
}

void SFC_WindowServer::Refresh()
{
	RefreshControl(this, 0, 0);
	USE_WINDOW_LIST(wlptr);
	for (auto wptr = wlptr->begin(); wptr != wlptr->end(); wptr++)
	{
		RefreshControl(*wptr, 0, 0);
	}
	// trace.Show(this->MainSurface);
	DrawCursor();

	OutsideSystem->UpdateWindow(this->MainSurface);
}

void SFC_WindowServer::RefreshControl(SFC_Object *Object, int x, int y)
{
	if (Object->Visiable == false) return ;
	Object->Paint();
	x += Object->Size.x; y += Object->Size.y;
	SFC_Rect rect = { x, y, Object->Size.w, Object->Size.h };
	BlitSurface(Object->Cache, nullptr, this->MainSurface, &rect);
	for (auto con = Object->Children.begin(); con != Object->Children.end(); con++)
	{
		RefreshControl(*con, x, y);
	}
}

void SFC_WindowServer::DrawCursor()
{
	SFC_Rect srect = {
		0, 0,
		Min(this->Size.w + this->Cursor->x, CURSOR_SIZE),
		Min(this->Size.h + this->Cursor->y, CURSOR_SIZE)
	};
	SFC_Rect drect = { this->Cursor->x, this->Cursor->y, CURSOR_SIZE, CURSOR_SIZE };
	BlitSurface(this->CursorSurface, &srect, this->MainSurface, &drect);
}

void SFC_WindowServer::CloseWindows()
{
	USE_WINDOW_LIST(wlptr);
	for (auto wptr = wlptr->begin(); wptr != wlptr->end(); wptr++)
	{
		(*wptr)->Close();
		(*wptr)->wthread.join();
	}
}
